package com.hs.velocityroo;

import static org.springframework.roo.project.Path.SRC_MAIN_WEBAPP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.addon.web.mvc.controller.WebMvcOperations;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.details.MemberFindingUtils;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.project.FeatureNames;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.DependencyScope;
import org.springframework.roo.project.DependencyType;
import org.springframework.roo.project.Repository;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

/**
 * Implementation of operations this add-on offers.
 *
 * @since 1.1
 */
@Component // Use these Apache Felix annotations to register your commands class in the Roo container
@Service
public class VelocityrooOperationsImpl implements VelocityrooOperations {
    
    /**
     * Use ProjectOperations to install new dependencies, plugins, properties, etc into the project configuration
     */
    @Reference private ProjectOperations projectOperations;

    /**
     * Use TypeLocationService to find types which are annotated with a given annotation in the project
     */
    @Reference private TypeLocationService typeLocationService;
    
    /**
     * Use TypeManagementService to change types
     */
    @Reference private TypeManagementService typeManagementService;
    @Reference private WebMvcOperations webMvcOperations;
    @Reference private PathResolver pathResolver;
	 @Reference private FileManager fileManager;
    /** {@inheritDoc} */
    public boolean isCommandAvailable() {
        // Check if a project has been created
        return projectOperations.isFocusedProjectAvailable();
    }

    /** {@inheritDoc} */
    public void annotateType(JavaType javaType) {
        // Use Roo's Assert type for null checks
        Validate.notNull(javaType, "Java type required");

        // Obtain ClassOrInterfaceTypeDetails for this java type
        ClassOrInterfaceTypeDetails existing = typeLocationService.getTypeDetails(javaType);

        // Test if the annotation already exists on the target type
        if (existing != null && MemberFindingUtils.getAnnotationOfType(existing.getAnnotations(), new JavaType(RooVelocityroo.class.getName())) == null) {
            ClassOrInterfaceTypeDetailsBuilder classOrInterfaceTypeDetailsBuilder = new ClassOrInterfaceTypeDetailsBuilder(existing);
            
            // Create JavaType instance for the add-ons trigger annotation
            JavaType rooRooVelocityroo = new JavaType(RooVelocityroo.class.getName());

            // Create Annotation metadata
            AnnotationMetadataBuilder annotationBuilder = new AnnotationMetadataBuilder(rooRooVelocityroo);
            
            // Add annotation to target type
            classOrInterfaceTypeDetailsBuilder.addAnnotation(annotationBuilder.build());
            
            // Save changes to disk
            typeManagementService.createOrUpdateTypeOnDisk(classOrInterfaceTypeDetailsBuilder.build());
        }
    }

    /** {@inheritDoc} */
    public void annotateAll() {
        // Use the TypeLocationService to scan project for all types with a specific annotation
        for (JavaType type: typeLocationService.findTypesWithAnnotation(new JavaType("org.springframework.roo.addon.javabean.RooJavaBean"))) {
            annotateType(type);
        }
    }
    
    /** {@inheritDoc} */
    public void setup(){
    	setup(projectOperations.getFocusedModuleName());
    }
    public void setup(final String moduleName){
  	  Validate.isTrue(isProjectAvailable(), "Project metadata required");
        final LogicalPath webappPath = Path.SRC_MAIN_WEBAPP
                .getModulePathId(moduleName);
        if (!isControllerAvailable()) {
            webMvcOperations.installAllWebMvcArtifacts();
        }
        
     // Install velocity config
        updateConfiguration();
        
        
        final String webRelativeFlowPath = "/WEB-INF/velocity/";
        final String resolvedFlowPath = pathResolver.getFocusedIdentifier(
                Path.SRC_MAIN_WEBAPP, webRelativeFlowPath);
      

        if (fileManager.exists(resolvedFlowPath)) {
            throw new IllegalStateException("Flow directory already exists: "
                    + resolvedFlowPath);
        }
        fileManager.createDirectory(resolvedFlowPath);
        
     // Install styles
        copyTemplate("css/reset.css", pathResolver.getIdentifier(webappPath, ""));
        copyTemplate("css/style.css", pathResolver.getIdentifier(webappPath, ""));
        
     // Install layout
        copyTemplate("velocity/index.vm",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("velocity/layout/header.vm",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("velocity/layout/footer.vm",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("i18n/application.properties",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("i18n/messages_en.properties",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("i18n/messages_pl.properties",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
        copyTemplate("i18n/messages.properties",
                pathResolver.getIdentifier(webappPath, "WEB-INF/"));
      //  copyTemplate("tiles/views.xml", pathResolver.getIdentifier(webappPath, "WEB-INF/views/"));

        
  }
    
    private boolean isProjectAvailable() {
        return projectOperations.isFocusedProjectAvailable();
    }
    public boolean isControllerAvailable() {
        return fileManager.exists(pathResolver.getFocusedIdentifier(
                Path.SRC_MAIN_WEBAPP, "WEB-INF/views"))
                && !projectOperations
                        .isFeatureInstalledInFocusedModule(FeatureNames.JSF);
    }
    private void copyTemplate(final String templateFileName,
            final String resolvedTargetDirectoryPath) {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        try {
            inputStream = FileUtils
                    .getInputStream(getClass(), templateFileName);
            outputStream = fileManager.createFile(
                    resolvedTargetDirectoryPath + "/" + templateFileName)
                    .getOutputStream();
            IOUtils.copy(inputStream, outputStream);
        }
        catch (final IOException e) {
            throw new IllegalStateException(
                    "Encountered an error during copying of resources for Web Flow addon.",
                    e);
        }
        finally {
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(outputStream);
        }
    }
    
    private void updateConfiguration() {
        // Add velocity dependencies to pom
        final Element configuration = XmlUtils.getRootElement(getClass(),
                "velocity/configuration.xml");

        final List<Dependency> dependencies = new ArrayList<Dependency>();
        final List<Element> springDependencies = XmlUtils.findElements(
                "/configuration/velocity/dependencies/dependency", configuration);
        for (final Element dependencyElement : springDependencies) {
            dependencies.add(new Dependency(dependencyElement));
        }
        projectOperations.addDependencies(
                projectOperations.getFocusedModuleName(), dependencies);

        // Add config to MVC app context
     final String mvcConfig = pathResolver.getFocusedIdentifier(
                SRC_MAIN_WEBAPP, "WEB-INF/spring/webmvc-config.xml");
        final Document mvcConfigDocument = XmlUtils.readXml(fileManager
                .getInputStream(mvcConfig));
        final Element beans = mvcConfigDocument.getDocumentElement();

        if (XmlUtils.findFirstElement("/beans/bean[@id = 'viewResolver']",
                beans) != null
                || XmlUtils.findFirstElement(
                        "/beans/bean[@id = 'velocityConfig']", beans) != null) {
            return; // velocity is already configured, nothing to do
        }

       // final Document configDoc = getDocumentTemplate("velocity/velocity-config.xml");
        final Element configElement = XmlUtils.getRootElement(getClass(),
                "velocity/velocity-config.xml");
        final List<Element> velocityConfig = XmlUtils.findElements("/config/bean",
                configElement);
        for (final Element bean : velocityConfig) {
            final Node importedBean = mvcConfigDocument.importNode(bean, true);
            beans.appendChild(importedBean);
        }
        fileManager.createOrUpdateTextFileIfRequired(mvcConfig,
                XmlUtils.nodeToString(mvcConfigDocument), true);
    }
    
}